package jenetics.test;

import java.util.stream.IntStream;

import org.jenetics.EnumGene;
import org.jenetics.Genotype;
import org.jenetics.Optimize;
import org.jenetics.PartiallyMatchedCrossover;
import org.jenetics.PermutationChromosome;
import org.jenetics.Phenotype;
import org.jenetics.SwapMutator;
import org.jenetics.engine.Engine;
import org.jenetics.engine.EvolutionStatistics;

import static org.jenetics.engine.EvolutionResult.toBestPhenotype;
import static org.jenetics.engine.limit.bySteadyFitness;

public class TravelingSalesman {

	private static final int STOPS = 20;
	private static final double[][] ADJACENCE = matrix(STOPS);

	public static void main(String[] args) {
		final Engine<EnumGene<Integer>, Double> engine = Engine.builder(TravelingSalesman::dist, PermutationChromosome.ofInteger(STOPS)).optimize(Optimize.MINIMUM).maximalPhenotypeAge(11).populationSize(500)
				.alterers(new SwapMutator<>(0.2), new PartiallyMatchedCrossover<>(0.35)).build();
		
		final EvolutionStatistics<Double, ?> statistics = EvolutionStatistics.ofNumber();
		
		final Phenotype<EnumGene<Integer>, Double> best = engine.stream().limit(bySteadyFitness(15)).limit(250).peek(statistics).collect(toBestPhenotype());
		
		System.out.println(statistics);
		System.out.println(best);
	}

	private static double[][] matrix(int stops) {
		final double radius = 10.0;
		double[][] matrix = new double[stops][stops];
		
		for (int i = 0; i < stops; ++i) {
			for (int j = 0; j < stops; ++j)
				matrix[i][j] = chord(stops, Math.abs(i - j), radius);
		}
		return matrix;
	}

	private static double chord(int stops, int i, double r) {
		return 2.0 * r * Math.abs(Math.sin(Math.PI * i / stops));
	}

	private static Double dist(final Genotype<EnumGene<Integer>> gt) {
		final int[] path = gt.getChromosome().toSeq().stream().mapToInt(EnumGene<Integer>::getAllele).toArray();
		
		return IntStream.range(0, STOPS).mapToDouble(i -> ADJACENCE [path[i]][path[(i + 1) % STOPS]]).sum();
	}
}